package com.example.utils;

import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.codec.Base64Encoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.buf.HexUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;

/**
 * @Describe : SM4加解密工具类
 * @Author : zyx
 * @Date : 2023/9/22 14:46
 * @Version : 1.0
 **/
@Component
@Slf4j
public class SM4Util {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * sm4 密钥空间大小
     */
    public static final int SM4_KEY_SIZE = 128;
    /**
     * sm4 算法编号
     */
    public static final String ALGORITHM_NAME = "SM4";
    /**
     * sm4 首次加密初始向量
     */
    public static final byte[] SM4_KEY_IV = {1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31};
    /**
     * sm4 ECB模式
     */
    public static final String ALGORITHM_ECB_PADDING = "SM4/ECB/PKCS5Padding";
    /**
     * sm4 CBC模式
     */
    public static final String ALGORITHM_CBC_PADDING = "SM4/CBC/PKCS5Padding";


    /**
     * 生成ECB暗号
     * @param algorithmName 算法名称
     * @param mode 模式
     * @param key
     * @return
     * @throws Exception
     */
    private static Cipher generateEcbCipher(String algorithmName, int mode , byte[] key) throws Exception{
        Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        cipher.init(mode,sm4Key);
        return cipher;
    }

    /**
     * 自动生成密钥
     * @return
     * @throws Exception
     */
    public static byte[] generateKey() throws Exception{
        return generateKey(SM4_KEY_SIZE);
    }

    /**
     *
     * @param keySize
     * @return
     * @throws Exception
     */
    public static byte[] generateKey(int keySize) throws Exception{
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(keySize,new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    public static String encryptEcb(String hexKey,String paramStr)throws Exception{
        String cipherText = "";
        // 16 --> byte[]
        byte[] keyData = HexUtils.fromHexString(hexKey);
        // string --> byte[]
        byte[] srcData = paramStr.getBytes(StandardCharsets.UTF_8);
        // 加密后的数组
        byte[] cipherArray = encryptEcbPadding(keyData, srcData);
        // byte[] --> hexString
//        cipherText = HexUtils.toHexString(cipherArray);
        cipherText = Base64Encoder.encode(cipherArray);
        return cipherText;
    }

    /**
     * ECB加密模式
     * @param key
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] encryptEcbPadding(byte[] key,byte[] data)throws Exception{
        Cipher cipher = generateEcbCipher(ALGORITHM_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal();
    }

    /**
     * sm4解密
     * @param hexKey 16进制密钥
     * @param cipherText 16进制的加密字符串
     * @return
     * @throws Exception
     */
    public static String decryptEcb(String hexKey,String cipherText) throws Exception{
        // 用于接收解密后的字符串
        String decryptStr = "";
        // hexString --> byte[]
        byte[] keyData = HexUtils.fromHexString(hexKey);
        // hexString --> byte[]
//        byte[] cipherData = HexUtils.fromHexString(cipherText);
        byte[] cipherData = Base64Decoder.decode(cipherText);
        // 解密
        byte[] srcData = decryptEcbPadding(keyData, cipherData);
        // byte[] --> string
        decryptStr = new String(srcData,"UTF-8");
        return decryptStr;
    }

    /**
     * 解密
     * @param key
     * @param cipherText
     * @return
     * @throws Exception
     */
    public static byte[] decryptEcbPadding(byte[] key,byte[] cipherText) throws Exception{
        Cipher cipher = generateEcbCipher(ALGORITHM_ECB_PADDING, Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherText);
    }

    /**
     * 校验加密前后的字符串是否为同一数据
     * @param hexKey
     * @param cipherText
     * @param paramStr
     * @return
     * @throws Exception
     */
    public static boolean verifyEcb(String hexKey,String cipherText,String paramStr)throws Exception{
        // 用于接收校验结果
        boolean flag = false;
        // hexString --> byte[]
        byte[] keyData = HexUtils.fromHexString(hexKey);
        // hexString --> byte[]
        byte[] cipherData = Base64Decoder.decode(cipherText);
        // 解密
        byte[] decryptData = decryptEcbPadding(keyData, cipherData);
        // 将原字符串转换成byte[]
        byte[] srcData = paramStr.getBytes(StandardCharsets.UTF_8);
        // 判断两个数组是否一致
        flag = Arrays.equals(decryptData, srcData);
        return flag;
    }

    public static void main(String[] args) {

        try {
            String tel = "13028498388";
            // 自定义的32位16进制密钥
            String key = "975B7238C3824FB01234567890123456";
//            byte[] bytesKey = SM4Utils.generateKey();
//            String key = HexUtils.toHexString(bytesKey);
            log.info("生成的密钥：{}",key);
            String cipher = SM4Util.encryptEcb(key, tel);
            log.info("加密后:{}",cipher);
            log.info(String.valueOf(SM4Util.verifyEcb(key,cipher,tel)));
            tel = SM4Util.decryptEcb(key,cipher);
            log.info("tel:{}",tel);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
